using System;
using System.Collections.Generic;

namespace BrainTechLLC
{
#if CACHED_LIST_SUPPORT
    /// <summary>
    /// Provides a strongly-typed (int ID, string name, and object) mapping
    /// </summary>
    public class IDAndNameMapping : IDNameObjectMapping<int, string, IDAndNameMapping>
    {
        public IDAndNameMapping(int ky, string nam, IDAndNameMapping o) : base(ky, nam, o) { }
    }
    /// <summary>
    /// Handles a simple cached list (get and invalidate)
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class CachedList<T> : ICrossObjectMappingBase where T : class
    {
        public delegate List<T> PopulateCacheFunction();
        private List<T> _cachedList = null;
        private object _lockObj = new object();
        private DateTime _lastRetrieved = DateTime.MinValue;
        /// <summary>
        /// Returns the cached list.  Takes a delegate that will populate a List<typeparamref name="T"/> if there is nothing in cache.
        /// </summary>
        /// <param name="fn"></param>
        /// <returns></returns>
        public List<T> GetCachedList(PopulateCacheFunction fn)
        {
            if (_cachedList == null || DateTime.Now.Date > _lastRetrieved.Date) { lock (_lockObj) { if (_cachedList == null || DateTime.Now.Date > _lastRetrieved.Date) { _cachedList = fn(); } } }
            return _cachedList;
        }
        public void InvalidateCache() { lock (_lockObj) { _cachedList = null; } }
    }
    /// <summary>
    /// Provides a quick lookup field where a single key maps to a single field (example: Quickly look up a member using their MENCID)
    /// </summary>
    /// <typeparam name="T1"></typeparam>
    /// <typeparam name="T2"></typeparam>
    public class QuickFieldLookup<T1, T2> : ICrossObjectMappingBase
    {
        internal Dictionary<T1, T2> _lookup = null;
        internal object _lockObj = new object();
        /// <summary>
        /// Removes the mapping between a key and an old value, and adds a mapping between the key and a new value
        /// </summary>
        /// <param name="oldval"></param>
        /// <param name="newval"></param>
        /// <param name="newobj"></param>
        public void UpdateMapping(T1 oldval, T1 newval, T2 newobj)
        {
            lock (_lockObj)
            {
                if (_lookup == null) { _lookup = new Dictionary<T1, T2>(512); }
                if (oldval != null)
                {
                    // Remove the old key, if any
                    if (_lookup.ContainsKey(oldval)) { _lookup.Remove(oldval); }
                }
                // Add the new/changed key
                if (!_lookup.ContainsKey(newval)) { _lookup.Add(newval, newobj); }
                else _lookup[newval] = newobj;
            }
        }
        public bool IsEmpty { get { return (_lookup == null || _lookup.Count == 0); } }
        /// <summary>
        /// Invalidates the cache
        /// </summary>
        public void InvalidateCache() { lock (_lockObj) { _lookup = null; } }
        /// <summary>
        /// Adds a simple mapping between a key and a value
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val"></param>
        public void AddMapping(T1 key, T2 val)
        {
            lock (_lockObj)
            {
                if (_lookup == null) { _lookup = new Dictionary<T1, T2>(512); }
                if (!_lookup.ContainsKey(key)) { _lookup.Add(key, val); }
            }
        }
        /// <summary>
        /// Removes a key and its map to a value
        /// </summary>
        /// <param name="key"></param>
        public void RemoveKey(T1 key)
        {
            if (_lookup != null)
            {
                lock (_lockObj) { if (_lookup != null) { if (_lookup.ContainsKey(key)) { _lookup.Remove(key); } } }
            }
        }
        /// <summary>
        /// Quickly looks up a value, given a key
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public bool PerformLookup(T1 key, ref T2 val)
        {
            if (_lookup != null)
            {
                lock (_lockObj) { if (_lookup != null) { if (!_lookup.TryGetValue(key, out val)) { return false; } else { return true; } } }
            }
            return false;
        }
    }
    /// <summary>
    /// A one-to-many lookup.  Example: one member ID may point to multiple teacher entries.
    /// </summary>
    /// <typeparam name="T1"></typeparam>
    /// <typeparam name="T2"></typeparam>
    public class OneToManyQuickFieldLookup<T1, T2> : ICrossObjectMappingBase
    {
        public Dictionary<T1, List<T2>> _lookup = null;
        public object _lockObj = new object();
        /// <summary>
        /// Updates the mapping, ensuring that the key is no longer linked with the old value, but is properly
        /// linked with the new value
        /// </summary>
        /// <param name="oldval"></param>
        /// <param name="newval"></param>
        /// <param name="newobj"></param>
        public void UpdateMapping(T1 oldval, T1 newval, T2 newobj)
        {
            lock (_lockObj)
            {
                if (_lookup == null) { _lookup = new Dictionary<T1, List<T2>>(512); }
                if (oldval != null)
                {
                    // Remove the old key, if any
                    if (_lookup.ContainsKey(oldval)) { _lookup.Remove(oldval); }
                }
                // Add the new/changed key and value
                if (!_lookup.ContainsKey(newval))
                {
                    List<T2> rg;
                    rg = new List<T2>(8);
                    rg.Add(newobj);
                    _lookup.Add(newval, rg);
                }
                else
                {
                    List<T2> rg;
                    if (_lookup.TryGetValue(newval, out rg) == true) { for (int i = 0; i < rg.Count; i++) { if (rg[i].Equals(newobj)) { rg.RemoveAt(i); break; } } }
                    else
                    {
                        rg = new List<T2>(8);
                        rg.Add(newobj);
                        _lookup[newval] = rg;
                    }
                }
            }
        }
        /// <summary>
        /// Invalidate the cache
        /// </summary>
        public void InvalidateCache() { lock (_lockObj) { _lookup = null; } }
        /// <summary>
        /// Adds multiple mappings between a key and multiple values (adds many new teacher records to be associated with an existing member ID)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="newvals"></param>
        public void AddMapping(T1 key, List<T2> newvals)
        {
            for (int i = 0; i < newvals.Count; i++)
            {
                AddMapping(key, newvals[i]);
            }
        }
        /// <summary>
        /// Adds a mapping between a key and a single value (add a new teacher record to an existing member ID)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val"></param>
        public void AddMapping(T1 key, T2 val)
        {
            lock (_lockObj)
            {
                if (_lookup == null) { _lookup = new Dictionary<T1, List<T2>>(512); }
                if (!_lookup.ContainsKey(key))
                {
                    List<T2> rg = new List<T2>(8);
                    rg.Add(val);
                    _lookup.Add(key, rg);
                }
                else
                {
                    List<T2> rg = _lookup[key];
                    bool fAlreadyThere = false;
                    for (int i = 0; i < rg.Count; i++)
                    {
                        if (rg[i].Equals(val)) { fAlreadyThere = true; break; }
                    }
                    if (!fAlreadyThere) rg.Add(val);
                }
            }
        }
        /// <summary>
        /// Removes a lookup key from the collection
        /// </summary>
        /// <param name="key"></param>
        public void RemoveKey(T1 key)
        {
            if (_lookup != null)
            {
                lock (_lockObj) { if (_lookup != null) { if (_lookup.ContainsKey(key)) { _lookup.Remove(key); } } }
            }
        }
        /// <summary>
        /// Performs a lookup using a key (i.e. Member ID), and returns a list of all associated objects (Teacher entries)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public bool PerformLookup(T1 key, ref List<T2> val)
        {
            if (_lookup != null)
            {
                lock (_lockObj) { if (_lookup != null) { if (!_lookup.TryGetValue(key, out val)) { return false; } else { return true; } } }
            }
            return false;
        }
    }
    /// <summary>
    /// Multiple-key caching and quick lookup.
    /// </summary>
    /// <typeparam name="T1">Type of key 1</typeparam>
    /// <typeparam name="T2">Type of key 2</typeparam>
    /// <typeparam name="T3">Type of the object being stored with keys 1 and 2</typeparam>
    public class MultikeyLookup<T1, T2, T3> : ICrossObjectMappingBase
    {
        public Dictionary<T1, Dictionary<T2, T3>> _lookup = null;
        public object _lockObj = new object();
        internal void CheckStructure() { if (_lookup == null) { lock (_lockObj) { if (_lookup == null) _lookup = new Dictionary<T1, Dictionary<T2, T3>>(64); } } }
        /// <summary>
        /// Invalidate the cache
        /// </summary>
        public void InvalidateCache() { lock (_lockObj) { _lookup = null; } }
        /// <summary>
        /// Adds multiple mappings between a key and multiple values (adds many new teacher records to be associated with an existing member ID)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="newvals"></param>
        public void AddOrSet(T1 key1, T2 key2, T3 obj)
        {
            Dictionary<T2, T3> secondkeyLookup = null;
            T3 oldObj;
            CheckStructure();
            lock (_lockObj)
            {
                if (!_lookup.TryGetValue(key1, out secondkeyLookup))
                {
                    secondkeyLookup = new Dictionary<T2, T3>(32);
                    secondkeyLookup.Add(key2, obj);
                    _lookup.Add(key1, secondkeyLookup);
                }
                else if (!secondkeyLookup.TryGetValue(key2, out oldObj)) { secondkeyLookup.Add(key2, obj); }
                else { secondkeyLookup[key2] = obj; }
            }
        }
        public T3 this[T1 key1, T2 key2]
        {
            get { CheckStructure(); T3 obj; if (PerformLookup(key1, key2, out obj)) return obj; else return default(T3); }
            set { CheckStructure(); AddOrSet(key1, key2, value); }
        }
        public bool PerformLookup(T1 key1, T2 key2, out T3 obj)
        {
            Dictionary<T2, T3> secondkeyLookup = null;
            CheckStructure();
            obj = default(T3);
            bool fFound = false;
            lock (_lockObj) { if (_lookup.TryGetValue(key1, out secondkeyLookup)) { if (secondkeyLookup.TryGetValue(key2, out obj)) fFound = true; } }
            return fFound;
        }
        /// <summary>
        /// Removes a lookup key from the collection
        /// </summary>
        /// <param name="key"></param>
        public void Remove(T1 key1, T2 key2)
        {
            Dictionary<T2, T3> secondkeyLookup = null;
            CheckStructure();
            lock (_lockObj) { if (_lookup.TryGetValue(key1, out secondkeyLookup)) { if (secondkeyLookup.ContainsKey(key2)) secondkeyLookup.Remove(key2); } }
        }
    }
    /// <summary>
    /// Multiple-key caching and quick lookup.
    /// </summary>
    /// <typeparam name="T1">Type of key 1</typeparam>
    /// <typeparam name="T2">Type of key 2</typeparam>
    /// <typeparam name="T3">Type of the object being stored with keys 1 and 2</typeparam>
    public class MultikeyListLookup<T1, T2, T3> : ICrossObjectMappingBase
    {
        public Dictionary<T1, OneToManyQuickFieldLookup<T2, T3>> _lookup = null;
        public object _lockObj = new object();
        internal void CheckStructure() { if (_lookup == null) { lock (_lockObj) { if (_lookup == null) _lookup = new Dictionary<T1, OneToManyQuickFieldLookup<T2, T3>>(64); } } }
        /// <summary>
        /// Invalidate the cache
        /// </summary>
        public void InvalidateCache() { lock (_lockObj) { _lookup = null; } }
        /// <summary>
        /// Adds multiple mappings between a key and multiple values (adds many new teacher records to be associated with an existing member ID)
        /// </summary>
        /// <param name="key"></param>
        /// <param name="newvals"></param>
        public void Set(T1 key1, T2 key2, List<T3> obj)
        {
            OneToManyQuickFieldLookup<T2, T3> secondkeyLookup = null;
            List<T3> oldObj = null;
            CheckStructure();
            lock (_lockObj)
            {
                if (!_lookup.TryGetValue(key1, out secondkeyLookup))
                {
                    secondkeyLookup = new OneToManyQuickFieldLookup<T2, T3>();
                    secondkeyLookup.AddMapping(key2, obj);
                    _lookup.Add(key1, secondkeyLookup);
                }
                else if (!secondkeyLookup.PerformLookup(key2, ref oldObj)) { secondkeyLookup.AddMapping(key2, obj); }
                else { secondkeyLookup.AddMapping(key2, obj); }
            }
        }
        public List<T3> this[T1 key1, T2 key2]
        {
            get { CheckStructure(); List<T3> obj; if (PerformLookup(key1, key2, out obj)) return obj; else return new List<T3>(); }
            set { CheckStructure(); Set(key1, key2, value); }
        }
        public bool PerformLookup(T1 key1, T2 key2, out List<T3> obj)
        {
            OneToManyQuickFieldLookup<T2, T3> secondkeyLookup = null;
            CheckStructure();
            obj = new List<T3>();
            bool fFound = false;
            lock (_lockObj)
            {
                if (_lookup.TryGetValue(key1, out secondkeyLookup))
                {
                    if (secondkeyLookup.PerformLookup(key2, ref obj)) fFound = true;
                }
            }
            return fFound;
        }
        /// <summary>
        /// Removes a lookup key from the collection
        /// </summary>
        /// <param name="key"></param>
        public void Remove(T1 key1, T2 key2)
        {
            OneToManyQuickFieldLookup<T2, T3> secondkeyLookup = null;
            CheckStructure();
            lock (_lockObj)
            {
                if (_lookup.TryGetValue(key1, out secondkeyLookup))
                {
                    List<T3> val = null;
                    if (secondkeyLookup.PerformLookup(key2, ref val)) secondkeyLookup.RemoveKey(key2);
                }
            }
        }
    }
#endif
}
